package com.cmc6.user.service.impl;

import cn.hutool.core.util.DesensitizedUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.cmc6.common.config.BaseConfig;
import com.cmc6.common.constant.BaseConstant;
import com.cmc6.common.exception.BaseBizCodeEnum;
import com.cmc6.common.model.dto.NotEmptyIdSet;
import com.cmc6.common.model.entity.BaseEntity;
import com.cmc6.common.util.*;
import com.cmc6.request.model.entity.RequestDO;
import com.cmc6.request.service.RequestService;
import com.cmc6.user.exception.BizCodeEnum;
import com.cmc6.user.mapper.UserMapper;
import com.cmc6.user.model.dto.*;
import com.cmc6.user.model.entity.UserInfoDO;
import com.cmc6.user.model.entity.UserLoginDO;
import com.cmc6.user.model.vo.UserCenterBaseInfoVO;
import com.cmc6.user.service.*;
import com.cmc6.user.util.PasswordConverterUtil;
import lombok.SneakyThrows;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Collections;
import java.util.Date;

@Service
public class UserCenterServiceImpl implements UserCenterService {

    @Resource
    UserInfoService userInfoService;
    @Resource
    UserLoginService userLoginService;
    @Resource
    UserService userService;
    @Resource
    RedissonClient redissonClient;
    @Resource
    UserRegHelperService regHelperService;
    @Resource
    RequestService requestService;
    @Resource
    HttpServletRequest request;
    @Resource
    RegByEmailService regByEmailService;
    @Resource
    UserMapper userMapper;
    @Resource
    UserRegHelperService userRegHelperService;

    /**
     * 用户基本信息
     */
    @Override
    public UserCenterBaseInfoVO baseInfo() {

        Long userId = UserUtil.getCurrentUserId();

        if (BaseConstant.ADMIN_ID.equals(userId)) {
            UserCenterBaseInfoVO resVO = new UserCenterBaseInfoVO();
            resVO.setAvatarUrl("");
            resVO.setNickname(BaseConfig.adminUsername);
            resVO.setPersonalStatement("");
            resVO.setEmail("");
            resVO.setPhone("");
            resVO.setPasswordFlag(true);
            return resVO;
        }

        UserCenterBaseInfoVO resVO = userMapper.baseInfo(userId);
        if (resVO != null) {
            resVO.setEmail(DesensitizedUtil.email(resVO.getEmail())); // 脱敏
            resVO.setPhone(DesensitizedUtil.mobilePhone(resVO.getPhone())); // 脱敏
        }

        return resVO;
    }

    /**
     * 修改用户基本信息
     */
    @Override
    @Transactional
    public String updateBaseInfo(UserCenterUpdateBaseInfoDTO dto) {

        Long userId = UserUtil.getCurrentUserId();
        if (BaseConstant.ADMIN_ID.equals(userId)) {
            ApiResult.error(BaseBizCodeEnum.THE_ADMIN_ACCOUNT_DOES_NOT_SUPPORT_THIS_OPERATION);
        }

        userInfoService.lambdaUpdate().set(UserInfoDO::getAvatarUrl, MyEntityUtil.getNotNullStr(dto.getAvatarUrl()))
            .set(UserInfoDO::getNickname, dto.getNickname())
            .set(UserInfoDO::getPersonalStatement, MyEntityUtil.getNotNullStr(dto.getPersonalStatement()))
            .eq(UserInfoDO::getUserId, userId).update();

        return BaseBizCodeEnum.API_RESULT_OK.getMsg();
    }

    /**
     * 刷新用户 jwt私钥后缀
     */
    @Override
    @Transactional
    public String refreshJwtSecretSuf() {

        Long userId = UserUtil.getCurrentUserId();
        if (BaseConstant.ADMIN_ID.equals(userId)) {
            ApiResult.error(BaseBizCodeEnum.THE_ADMIN_ACCOUNT_DOES_NOT_SUPPORT_THIS_OPERATION);
        }

        NotEmptyIdSet notEmptyIdSet = new NotEmptyIdSet();
        notEmptyIdSet.setIdSet(Collections.singleton(userId));

        userService.refreshJwtSecretSuf(notEmptyIdSet);

        return "刷新成功";
    }

    /**
     * 修改密码
     */
    @Override
    @Transactional
    public String updatePassword(UserCenterUpdatePasswordDTO dto) {

        Long userId = UserUtil.getCurrentUserId();
        if (BaseConstant.ADMIN_ID.equals(userId)) {
            ApiResult.error(BaseBizCodeEnum.THE_ADMIN_ACCOUNT_DOES_NOT_SUPPORT_THIS_OPERATION);
        }

        // 非对称加密，解密 ↓
        String paramValue = ParamUtil.getValueById(BaseConstant.RSA_PRIVATE_KEY_ID); // 获取非对称加密，私钥
        dto.setNewOrigPassword(userService.rsaDecrypt(dto.getNewOrigPassword(), paramValue));
        dto.setOldPassword(userService.rsaDecrypt(dto.getOldPassword(), paramValue));
        dto.setNewPassword(userService.rsaDecrypt(dto.getNewPassword(), paramValue));
        // 非对称加密，解密 ↑

        userService.checkOrigPassword(dto.getNewOrigPassword()); // 检查密码是否合法

        UserLoginDO userLoginDO =
            userLoginService.lambdaQuery().eq(UserLoginDO::getUserId, userId).select(UserLoginDO::getPassword).one();

        if (StrUtil.isBlank(userLoginDO.getPassword())) {
            ApiResult.error(BizCodeEnum.NO_PASSWORD_SET_FOR_USER_CENTER);
        }

        // 校验密码
        if (!PasswordConverterUtil.match(userLoginDO.getPassword(), dto.getOldPassword())) {
            ApiResult.error(BizCodeEnum.OLD_PASSWORD_ERROR);
        }

        // 不能和上次密码一样，备注：必须在 旧密码校验通过之后，才进行此校验，才好进行提示：不能和上次密码一样
        if (dto.getOldPassword().equals(dto.getNewPassword())) {
            ApiResult.error(BizCodeEnum.CAN_NOT_BE_THE_SAME_AS_THE_LAST_PASSWORD);
        }

        // 更新数据库的密码
        String newPassword = PasswordConverterUtil.converter(dto.getNewPassword(), true);
        userLoginService.lambdaUpdate().eq(UserLoginDO::getUserId, userId).set(UserLoginDO::getPassword, newPassword)
            .set(UserLoginDO::getUpdateTime, new Date()).set(UserLoginDO::getUpdateId, userId).update();

        refreshJwtSecretSuf(); // 下线该账号：刷新 jwt私钥后缀

        return "修改成功";
    }

    /**
     * 修改邮箱
     */
    @Override
    @Transactional
    public String updateEmail(UserCenterUpdateEmailDTO dto) {

        Long userId = UserUtil.getCurrentUserId();
        if (BaseConstant.ADMIN_ID.equals(userId)) {
            ApiResult.error(BaseBizCodeEnum.THE_ADMIN_ACCOUNT_DOES_NOT_SUPPORT_THIS_OPERATION);
        }

        String key = BaseConstant.PRE_LOCK_EMAIL_REG_CODE + dto.getEmail();

        // 增加分布式锁
        // 获取一把锁，只要锁名字一样，就是同一把锁，即：同一时间只能有一个邮箱进行 注册/修改
        RLock lock = redissonClient.getLock(BaseConstant.PRE_REDISSON + key);
        lock.lock();

        try {

            // 检查密码的正确
            checkPassword(dto.getPassword(), userId);

            regHelperService.checkCodeByRedis(key, dto.getCode());

            // 操作数据库
            userLoginService.lambdaUpdate().eq(UserLoginDO::getUserId, userId)
                .set(UserLoginDO::getEmail, dto.getEmail()).set(UserLoginDO::getUpdateId, userId)
                .set(UserLoginDO::getUpdateTime, new Date()).update();

            return "修改成功";
        } finally {
            // 解除分布式锁
            lock.unlock();
        }
    }

    /**
     * 检查密码的正确
     */
    private void checkPassword(String password, Long userId) {

        UserLoginDO selectOne =
            userLoginService.lambdaQuery().eq(UserLoginDO::getUserId, userId).select(UserLoginDO::getPassword).one();
        if (StrUtil.isBlank(selectOne.getPassword())) {
            ApiResult.error(BizCodeEnum.NO_PASSWORD_SET_FOR_USER_CENTER);
        }

        password = userService.rsaDecrypt(password);// 非对称加密：解密

        if (!PasswordConverterUtil.match(selectOne.getPassword(), password)) { // 校验密码
            ApiResult.error(BizCodeEnum.PASSWORD_ERROR);
        }

    }

    /**
     * 邮箱修改：发送验证码
     */
    @Override
    public String updateEmailSendCode(EmailNotBlankDTO dto) {
        return regByEmailService.sendCode(dto, 2);
    }

    /**
     * 邮箱绑定：发送验证码
     */
    @Override
    public String bindEmailSendCode(EmailNotBlankDTO dto) {
        return regByEmailService.sendCode(dto, 3);
    }

    /**
     * 修改手机号
     */
    @Override
    @Transactional
    public String updatePhone(UserCenterUpdatePhoneDTO dto) {

        Long userId = UserUtil.getCurrentUserId();
        if (BaseConstant.ADMIN_ID.equals(userId)) {
            ApiResult.error(BaseBizCodeEnum.THE_ADMIN_ACCOUNT_DOES_NOT_SUPPORT_THIS_OPERATION);
        }

        String key = BaseConstant.PRE_LOCK_PHONE_REG_CODE + dto.getPhone();

        // 增加分布式锁
        // 获取一把锁，只要锁名字一样，就是同一把锁，即：同一时间只能有一个手机进行 注册/修改
        RLock lock = redissonClient.getLock(BaseConstant.PRE_REDISSON + key);
        lock.lock();

        try {

            // 检查密码的正确
            checkPassword(dto.getPassword(), userId);

            regHelperService.checkCodeByRedis(key, dto.getCode());

            // 操作数据库
            userLoginService.lambdaUpdate().eq(UserLoginDO::getUserId, userId)
                .set(UserLoginDO::getPhone, dto.getPhone()).set(UserLoginDO::getUpdateId, userId)
                .set(UserLoginDO::getUpdateTime, new Date()).update();

            return "修改成功";
        } finally {
            // 解除分布式锁
            lock.unlock();
        }
    }

    /**
     * 修改手机号：发送验证码
     */
    @Override
    public String updatePhoneSendCode(PhoneNotBlankDTO dto) {
        return userRegHelperService.phoneSendCode(dto.getPhone(), 4);
    }

    /**
     * 手机绑定：发送验证码
     */
    @Override
    public String bindPhoneSendCode(PhoneNotBlankDTO dto) {
        return userRegHelperService.phoneSendCode(dto.getPhone(), 5);
    }

    /**
     * 查看登录记录
     */
    @Override
    public Page<RequestDO> loginRecord(UserCenterLoginRecordDTO dto) {
        dto.setCreateId(UserUtil.getCurrentUserId());
        dto.setUri("/login");

        return requestService.lambdaQuery().eq(BaseEntity::getCreateId, UserUtil.getCurrentUserId())
            .eq(RequestDO::getUri, "/login").page(dto.getPage());

    }

    /**
     * 账号注销
     */
    @SneakyThrows
    @Override
    @Transactional
    public String delAccount(UserCenterDelAccountDTO dto) {

        Long userId = UserUtil.getCurrentUserId();
        if (BaseConstant.ADMIN_ID.equals(userId)) {
            ApiResult.error(BaseBizCodeEnum.THE_ADMIN_ACCOUNT_DOES_NOT_SUPPORT_THIS_OPERATION);
        }

        checkPassword(dto.getPassword(), userId);

        // 删除数据
        NotEmptyIdSet notEmptyIdSet = new NotEmptyIdSet();
        notEmptyIdSet.setIdSet(Collections.singleton(userId));
        userService.deleteByIdSet(notEmptyIdSet);

        return "注销成功";
    }

    /**
     * 退出登录
     */
    @Override
    public String logout() {

        Long currentUserId = UserUtil.getCurrentUserId();

        // 清除 redis中的 jwt
        MyJwtUtil.removeJwtHashByRequestCategoryOrJwtHash(currentUserId, null,
            MyJwtUtil.generateRedisJwtHash(request.getHeader(BaseConstant.JWT_HEADER_KEY)), null);

        return "登出成功！";
    }

}
